package com.wicketfaces.tagcloud;

import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.AbstractRepeater;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.collections.ReadOnlyIterator;
import org.mcavallo.opencloud.Cloud;
import org.mcavallo.opencloud.Cloud.Case;
import org.mcavallo.opencloud.Tag;

public abstract class TagCloudPanel extends Panel {

	public static enum FONT_SIZE_TYPE {
		EM("em"), PT("pt"), PX("px");
		private static DecimalFormatSymbols symbols = new DecimalFormatSymbols();
		static{
			symbols.setDecimalSeparator('.');
			symbols.setGroupingSeparator(',');
		}
		private static NumberFormat intFormatter = new DecimalFormat("0");
		private static NumberFormat decFormatter = new DecimalFormat("0.00", symbols);
		private String name;
		
		private FONT_SIZE_TYPE(String name){
			this.name = name;
		}
		
		public String toString(){
			return this.name;
		}
		
		public String getFontStyle(double weight){
			switch(this){
			case EM : return "font-size: " + decFormatter.format(weight)+ "em;";
			case PX : return "font-size: " + intFormatter.format(weight)+ "px;";
			case PT : return "font-size: " + intFormatter.format(weight)+ "pt;";
			default : return "font-size: " + decFormatter.format(weight)+ "em;";
			}
		}
	}
	
	private static final long serialVersionUID = 4768954129876874361L;
	
	private HeaderContributor CSS = CSSPackageResource.getHeaderContribution(TagCloudPanel.class, "tagcloud.css");
	
	private List<Tag> tagList;

	private List<?> list;
	
	private int viewSize = Integer.MAX_VALUE;
	
	private Cloud cloud = new Cloud();
	
	private Case tagCase;
	
	private Class<? extends Page> pageClass;
	
	private int baseFontSize;
	private double maxWeight;
	private double minWeight;
	private FONT_SIZE_TYPE sizeType = FONT_SIZE_TYPE.EM;
	
	public TagCloudPanel(String id, Class<? extends Page> pageClass, List<?> list, Case tagCase) {
		super(id);
		this.list = list;
		this.pageClass = pageClass;
		this.setTagCase(tagCase);
		add(new TagRepeater("tagitem"));
		add(CSS);
	}
	
	public TagCloudPanel(String id, Class<? extends Page> pageClass) {
		super(id);
		this.pageClass = pageClass;
		add(new TagRepeater("tagitem"));
		add(CSS);
	}
	
	public void setCSS(HeaderContributor css){
		this.CSS = css;
	}
	
	public TagCloudPanel setBaseFontSize(int baseFontSize) {
		if(baseFontSize < 0){
			baseFontSize = 0;
		}
		this.baseFontSize = baseFontSize;
		return this;
	}

	public FONT_SIZE_TYPE getSizeType() {
		return sizeType;
	}

	public TagCloudPanel setSizeType(FONT_SIZE_TYPE sizeType) {
		this.sizeType = sizeType;
		return this;
	}
	
	public int getBaseFontSize() {
		return baseFontSize;
	}

	public double getMaxWeight() {
		return maxWeight;
	}

	public TagCloudPanel setMaxWeight(double maxWeight) {
		this.maxWeight = maxWeight;
		this.cloud.setMaxWeight(maxWeight);
		return this;
	}

	public double getMinWeight() {
		return minWeight;
	}

	public TagCloudPanel setMinWeight(double minWeight) {
		this.minWeight = minWeight;
		this.cloud.setMinWeight(minWeight);
		return this;
	}

	public final TagCloudPanel setViewSize(final int size){
		viewSize  = size;
		if(viewSize < 0){
			viewSize = Integer.MAX_VALUE;
		}
		return this;
	}
	
	public final TagCloudPanel setTagCase(Case tagCase){
		this.tagCase = tagCase;
		this.cloud.setTagCase(this.tagCase);
		return this;
	}
	
	public final int getViewSize(){
		
		if(this.tagList == null){
			return 0;
		}
		
		if(this.viewSize == Integer.MAX_VALUE || this.viewSize > this.tagList.size()){
			return this.tagList.size();
		}
		
		return this.viewSize;
	}
	
	public abstract void populate(TagItem item);

	private class TagRepeater extends AbstractRepeater {
	
		private static final long serialVersionUID = -3916473939446270389L;

		public TagRepeater(String id) {
			super(id);
		}

		@SuppressWarnings("unchecked")
		@Override
		protected final void onPopulate() {
			if(TagCloudPanel.this.list != null && !TagCloudPanel.this.list.isEmpty()){
				
				int index = 0;
				for(Object item : list){
					TagItem tagItem = new TagItem(index++, new Model((Serializable)item));
					populate(tagItem);
					cloud.addTag(tagItem.getTag());
				}
				TagCloudPanel.this.tagList = cloud.tags();
				for(Tag tag : TagCloudPanel.this.tagList){
					TagItem item = ((WicketTag)tag).getTagItem();
					Label label = new Label("tag", new Model<String>(item.getTag().getName()));
					label
							.add(new SimpleAttributeModifier("style", TagCloudPanel.this.getSizeType().getFontStyle(item.getTag().getWeight())));
					item.add(label);
					add(item);
				}
			}
		}
	
		/**
		 * @see org.apache.wicket.markup.repeater.AbstractRepeater#renderChild(org.apache.wicket.Component)
		 */
		@Override
		protected final void renderChild(Component child)
		{
			renderItem((TagItem)child);
		}
	
	
		/**
		 * Render a single item.
		 * 
		 * @param item
		 *            The item to be rendered
		 */
		protected void renderItem(final TagItem item)
		{
			item.render(getMarkupStream());
		}
		
		@Override
		protected Iterator<? extends Component> renderIterator() {
			final int size = getViewSize();
			return new ReadOnlyIterator<Component>()
			{
				private int index = 0;
				private int counter = 0;
	
				public boolean hasNext()
				{
					return counter < size;
				}
	
				public Component next()
				{
					final String id = Integer.toString(index);
					Component c = get(id);
					index++;
					if(c == null && hasNext()){
						return this.next();
					}
					counter++;
					return c;
				}
			};
		}
	}
	
	public static final class WicketTag extends Tag {

		private static final long serialVersionUID = 1L;  
		private TagItem tagItem;
		public TagItem getTagItem() {
			return this.tagItem;
		}
		public void setTagItem(TagItem tagItem) {
			this.tagItem = tagItem;
		}
		
		
	}
	
	public final class TagItem<T extends Serializable> extends BookmarkablePageLink<T> {
		@Override
		protected CharSequence getURL() {
			this.setParameter("tag", this.tag.getName().toLowerCase());
			return super.getURL();
		}

		private static final long serialVersionUID = -2100893533856404174L;
		
		private WicketTag tag;

		public TagItem(int id, IModel<T> model) {
			super(new Integer(id).toString().intern(), TagCloudPanel.this.pageClass);
			this.setModel(model);
			this.tag = new WicketTag();
			this.tag.setTagItem(this);
		}
		
		public void setDate(Date date) {
			tag.setDate(date);
		}

		public void setLink(String link) {
			tag.setLink(link);
		}

		public void setName(String name) {
			tag.setName(name);
		}

		public void setNormScore(double normScore) {
			tag.setNormScore(normScore);
		}

		public void setScore(double score) {
			tag.setScore(score);
		}

		public void setWeight(double weight) {
			tag.setWeight(weight);
		}
		
		public void setTag(WicketTag tag) {
			this.tag = tag;
		}

		public WicketTag getTag() {
			return tag;
		}
		
	}
	
	
	
	

}
